package top.someapp.applib.cast;

import java.math.BigDecimal;
import top.someapp.applib.kits.ClassMate;

/**
 * @author zw-zheng
 * Created on 2019-07-19
 */
public abstract class AbstractCastor<T> implements Castor<T> {

    public static BasicType guessBasicType(Object value) {
        for (BasicType type : BasicType.values()) {
            if (isBasicType(value, type)) {
                return type;
            }
        }
        return null;
    }

    public static boolean isBasicType(Object value) {
        return guessBasicType(value) != null;
    }

    public static boolean isBasicType(Object value, BasicType type) {
        Class<?> clz = value.getClass();
        return clz.equals(type.boxType()) || clz.equals(type.unBoxType());
    }

    @SuppressWarnings("unchecked")
    @Override
    public T cast(Object from, T defaultValue) throws IllegalArgumentException {
        if (from == null) {
            return defaultValue;
        }

        Class<?> toType = getTargetType();
        if (ClassMate.is(from, toType)) {
            return (T) from;
        }

        BasicType fromType = guessBasicType(from);
        if (fromType == null) {
            return doCast(from, toType, defaultValue);
        }

        if (toType.equals(Boolean.class) || toType.equals(boolean.class)) {
            if ("true".equals(from.toString()) || "1".equals(from.toString())) {
                return (T) Boolean.TRUE;
            }
            return (T) Boolean.FALSE;
        }

        if (ClassMate.isSubClassOf(toType, Number.class)) {
            Number number;
            if (from instanceof Number) {
                number = (Number) from;
            } else if (from instanceof String) {
                number = new BigDecimal(from.toString());
            } else {
                return null;
            }

            if (toType.equals(Number.class)) {
                return (T) number;
            } else if (toType.equals(Byte.class) || toType.equals(byte.class)) {
                return (T) Byte.valueOf(number.byteValue());
            } else if (toType.equals(Short.class) || toType.equals(short.class)) {
                return (T) Short.valueOf(number.shortValue());
            } else if (toType.equals(Integer.class) || toType.equals(int.class)) {
                return (T) Integer.valueOf(number.intValue());
            } else if (toType.equals(Long.class) || toType.equals(long.class)) {
                return (T) Long.valueOf(number.longValue());
            } else if (toType.equals(Float.class) || toType.equals(float.class)) {
                return (T) Float.valueOf(number.floatValue());
            } else if (toType.equals(Double.class) || toType.equals(double.class)) {
                return (T) Double.valueOf(number.doubleValue());
            }
        } else if (ClassMate.isSubClassOf(toType, String.class)) {
            return (T) String.valueOf(from);
        }
        return null;
    }

    protected T doCast(Object from, Class<?> toType, T defaultValue) {
        throw new ClassCastException(
            "AbstractCastor can not cast value: " + from + " to " + toType);
    }

    public Class<?> getTargetType() {
        return ClassMate.typeParameter(getClass(), 0);
    }
}
